#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "TcpServer.h"
#include "TcpConnection.h"
#include "Log.h"

struct TcpServer *tcpServerInit(uint16_t port, int32_t threadNum)
{
    struct TcpServer *tcpServer = (struct TcpServer *)malloc(sizeof(struct TcpServer));
    tcpServer->listener = listenerInit(port);
    tcpServer->mainLoop = eventLoopInit(EPOLL);
    tcpServer->threadNum = threadNum;
    tcpServer->threadPool = threadPoolInit(tcpServer->mainLoop, threadNum);
    return tcpServer;
}

struct Listener *listenerInit(uint16_t port)
{
    struct Listener *listener = (struct Listener *)malloc(sizeof(struct Listener));
    // 1.创建监听的fd
    int32_t lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (lfd == -1)
    {
        perror("socket");
        return NULL;
    }
    // 2.设置端口复用
    uint32_t opt = 1;
    int32_t ret = setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    if (ret == -1)
    {
        perror("setsockopt");
        return NULL;
    }
    // 3.绑定IP和端口
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;
    ret = bind(lfd, (struct sockaddr *)&addr, sizeof(addr));
    if (ret == -1)
    {
        perror("bind");
        return NULL;
    }
    // 4.设置监听
    ret = listen(lfd, 128);
    if (ret == -1)
    {
        perror("listen");
        return NULL;
    }
    printf("主线程监听成功 lf = %d\n", lfd);
    listener->port = port;
    listener->lfd = lfd;
    return listener;
}

int acceptConnection(void *arg)
{
    struct TcpServer *server = (struct TcpServer *)arg;
    // 和客户端建立连接
    int32_t cfd = accept(server->listener->lfd, NULL, NULL);
    printf("主线程 新连接cfd = %d\n", cfd);
    // 从线程池中取出一个子线程的反应堆实例，去处理这个cfd
    struct EventLoop *evLoop = takeWorkerEventLoop(server->threadPool); // 如果没有子线程 则返回主线程的mainEventLoop
    // 将cfd放到TcpConnetction中处理
    TcpConnectionInit(cfd, evLoop);
    return 0;
}

void tcpServerRun(struct TcpServer *server)
{
    // 启动线程池
    threadPollRun(server->threadPool);
    // 添加检测的任务
    struct Channel *channel = channelInit(server->listener->lfd, ReadEvent, acceptConnection, NULL, TcpConnectionDestroy, server);
    eventLoopAddTask(server->mainLoop, channel, ADD);
    // 启动反应堆模型
    eventLoopRun(server->mainLoop);
}
